package com.netintech.intelligenceInterface.iflytek.util;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

public class AuthUtils {

    public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
    public static final String ALGORITHM_NAME = "hmacsha256";
    public static final String DIGEST_NAME = "SHA-256";
    public static final String DATE_FORMAT_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";

    /**
     * 计算签名所需要的header参数 （http 接口）
     * @param requestUrl
     * @param apiKey
     * @param apiSecret
     * @param body
     * @return header map
     */
    public static Map<String, String> assembleRequestHeader(String httpMethod, String requestUrl, String apiKey, String apiSecret, byte[] body) {
        URL url = null;
        try {
            url = new URL(requestUrl);
            // 获取日期
            SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
            format.setTimeZone(TimeZone.getTimeZone("GMT"));
            String date = format.format(new Date());

            // 计算body 摘要(SHA256)
            String digest = null;
            if (body != null) {
                MessageDigest instance = MessageDigest.getInstance(DIGEST_NAME);
                instance.update(body);
                digest = "SHA-256=" + Base64.getEncoder().encodeToString(instance.digest());
            }

            String host = url.getHost();
            //构建签名计算所需参数
            StringBuilder builder = new StringBuilder().
                    append("host: ").append(host).append("\n").
                    append("date: ").append(date).append("\n").
                    append(httpMethod).append(" ").append(url.getPath()).append(" HTTP/1.1");
            Charset charset = Charset.forName("UTF-8");
            //使用hmac-sha256计算签名
            Mac mac = Mac.getInstance(ALGORITHM_NAME);
            System.out.println(builder.toString());
            SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), ALGORITHM_NAME);
            mac.init(spec);
            byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
            String sha = Base64.getEncoder().encodeToString(hexDigits);
            // 构建header
            String headers = StringUtils.isBlank(digest) ? "host date request-line" : "host date request-line digest";
            String authorization = String.format("hmac api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", headers, sha);
            Map<String, String> header = new HashMap<String, String>();
            header.put("authorization", authorization);
            header.put("host", host);
            header.put("date", date);
//            header.put("digest", digest);
            return header;
        } catch (Exception e) {
            throw new RuntimeException("assemble requestHeader error:" + e.getMessage());
        }
    }

    /**
     * 计算签名所需要的header参数 （http 接口）
     *
     * @param requestUrl
     * @param apiKey
     * @param apiSecret
     * @param body
     * @return header map
     */
    public static Map<String, String> assembleRequestHeader(String requestUrl, String apiKey, String apiSecret, String body) {
        URL url = null;
        try {
            url = new URL(requestUrl);
            // 获取日期
            SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT_PATTERN, Locale.US);
            format.setTimeZone(TimeZone.getTimeZone("GMT"));
            String date = format.format(new Date());
            //计算body 摘要(SHA256)
            MessageDigest instance = MessageDigest.getInstance(DIGEST_NAME);
            instance.update(body.getBytes(StandardCharsets.UTF_8));
            String digest = "SHA-256=" + Base64.getEncoder().encodeToString(instance.digest());
            String host = url.getHost();
            //构建签名计算所需参数
            StringBuilder builder = new StringBuilder().
                    append("host: ").append(host).append("\n").
                    append("date: ").append(date).append("\n").
                    append("POST ").append(url.getPath()).append(" HTTP/1.1");
            //使用hmac-sha256计算签名
            Mac mac = Mac.getInstance(ALGORITHM_NAME);
            SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(CHARSET_UTF8), ALGORITHM_NAME);
            mac.init(spec);
            byte[] hexDigits = mac.doFinal(builder.toString().getBytes(CHARSET_UTF8));
            String sha = Base64.getEncoder().encodeToString(hexDigits);
            // 构建header
            String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line digest", sha);
            Map<String, String> header = new HashMap<String, String>();
            header.put("Authorization", "hmac " + authorization);
            header.put("Host", host);
            header.put("Date", date);
            header.put("Digest", digest);
            return header;
        } catch (Exception e) {
            throw new RuntimeException("assemble requestHeader  error:" + e.getMessage());
        }
    }


    public static String assembleRequestUrl(String requestUrl, String apiKey, String apiSecret) {
        return assembleRequestUrl(HttpMethod.GET, requestUrl, apiKey, apiSecret);
    }

    public static String assembleRequestUrl(HttpMethod httpMethod, String requestUrl, String apiKey, String apiSecret) {
        return assembleRequestUrl(httpMethod, requestUrl, apiKey, apiSecret, null);
    }

    public static String assembleRequestUrl(HttpMethod httpMethod, String requestUrl, String apiKey, String apiSecret, String apiKeyKey) {
        AuthorizationData authorizationData = assemble(httpMethod, requestUrl, apiKey, apiSecret, null, apiKeyKey);

        try {
            String authBase = Base64.getEncoder().encodeToString(authorizationData.getAuthorization().getBytes(CHARSET_UTF8));
            authBase = String.format("%s?%s=%s&host=%s&date=%s", requestUrl, "Authorization".toLowerCase(), URLEncoder.encode(authBase, "utf-8"), URLEncoder.encode(authorizationData.getHost(), "utf-8"), URLEncoder.encode(authorizationData.getDate(), "utf-8"));
            return authBase;
        } catch (Exception var7) {
            throw new RuntimeException(var7);
        }
    }


    private static AuthorizationData assemble(HttpMethod httpMethod, String requestUrl, String apiKey, String apiSecret, String body, String apiKeyKey) {
        if (StringUtils.isBlank(requestUrl)) {
            throw new IllegalArgumentException("requestUrl is empty.");
        } else if (StringUtils.isBlank(apiKey)) {
            throw new IllegalArgumentException("apiKey is empty.");
        } else if (StringUtils.isBlank(apiSecret)) {
            throw new IllegalArgumentException("apiSecret is empty.");
        } else {
            String httpRequestUrl = requestUrl.replace("ws://", "http://").replace("wss://", "https://");

            try {
                URL url = new URL(httpRequestUrl);
                SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                String date = format.format(new Date());
                String sha = getSignature(url.getHost(), date, getRequestLine(httpMethod.toString(), url.getPath()), apiSecret);
                String digest = null;
                if (StringUtils.isNoneBlank(body)) {
                    digest = signBody(body);
                }

                apiKeyKey = StringUtils.isBlank(apiKeyKey) ? "hmac api_key" : apiKeyKey;
                String authorization = String.format("%s=\"%s\", algorithm=\"hmac-sha256\", headers=\"host date request-line%s\", signature=\"%s\"", apiKeyKey, apiKey, StringUtils.isBlank(digest) ? "" : " digest", sha);
                AuthorizationData authorizationData = new AuthorizationData();
                authorizationData.setDate(date).setHost(url.getHost()).setAuthorization(authorization).setDigest(digest);
                return authorizationData;
            } catch (Exception var14) {
                throw new RuntimeException(var14);
            }
        }
    }

    public static String getRequestLine(String method, String path) {
        return String.format("%s %s HTTP/1.1", method.toUpperCase(), path);
    }

    public static String getSignature(String host, String date, String requestLine, String apiSecret) {
        if (StringUtils.isBlank(host)) {
            throw new IllegalArgumentException("host is empty.");
        } else if (StringUtils.isBlank(date)) {
            throw new IllegalArgumentException("date is empty.");
        } else if (StringUtils.isBlank(requestLine)) {
            throw new IllegalArgumentException("requestLine is empty.");
        } else if (StringUtils.isBlank(apiSecret)) {
            throw new IllegalArgumentException("apiSecret is empty.");
        } else {
            try {
                URI uri = new URI("skynet://" + host);
                StringBuilder builder = (new StringBuilder("host: ")).append(uri.getHost()).append("\n").append("date: ").append(date).append("\n");
                builder.append(requestLine);
                Mac mac = Mac.getInstance("hmacsha256");
                SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(CHARSET_UTF8), "hmacsha256");
                mac.init(spec);
                byte[] hexDigits = mac.doFinal(builder.toString().getBytes(CHARSET_UTF8));
                String signature = Base64.getEncoder().encodeToString(hexDigits);
                return signature;
            } catch (Exception var10) {
                throw new RuntimeException(var10);
            }
        }
    }

    public static String signBody(String body) throws Exception {
        if (StringUtils.isBlank(body)) {
            throw new IllegalArgumentException("body is empty.");
        } else {
            try {
                return signBody(body.getBytes(StandardCharsets.UTF_8));
            } catch (UnsupportedEncodingException var2) {
                return null;
            }
        }
    }

    public static String signBody(byte[] body) throws Exception {
        if (body != null && body.length != 0) {
            String digest = "";
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
                messageDigest.update(body);
                digest = Base64.getEncoder().encodeToString(messageDigest.digest());
            } catch (NoSuchAlgorithmException var4) {
            }
            return digest;
        } else {
            throw new IllegalArgumentException("body is empty.");
        }
    }


    static class AuthorizationData {
        private String date;
        private String host;
        private String authorization;
        private String digest;

        AuthorizationData() {
        }

        public Map<String, String> getHeader() {
            Map<String, String> headers = new LinkedHashMap(4);
            headers.put("Host", this.host);
            headers.put("Date", this.date);
            if (StringUtils.isNoneBlank(new CharSequence[]{this.digest})) {
                headers.put("Digest", String.format("%s=%s", "SHA-256", this.digest));
            }

            headers.put("Authorization", this.authorization);
            return headers;
        }

        public String getDate() {
            return this.date;
        }

        public AuthorizationData setDate(String date) {
            this.date = date;
            return this;
        }

        public String getHost() {
            return this.host;
        }

        public AuthorizationData setHost(String host) {
            this.host = host;
            return this;
        }

        public String getAuthorization() {
            return this.authorization;
        }

        public AuthorizationData setAuthorization(String authorization) {
            this.authorization = authorization;
            return this;
        }

        public String getDigest() {
            return this.digest;
        }

        public AuthorizationData setDigest(String digest) {
            this.digest = digest;
            return this;
        }

        @Override
        public String toString() {
            return String.format("host=%s;date=%s;digest=%s;authorization=%s;", this.host, this.date, this.digest, this.authorization);
        }
    }


}
